/*
 * hashGen.cc
 *
 *  Created on: Dec 14, 2012
 *      Author: hxin
 */

#include "hashGen.h"
#include "common.h"
#include <fstream>

using namespace std;

void hashGen::loadHash(int chrNum) {

	int chrSize = countChr(chrNum);
	int progressCount = 0;

	cerr << "chrSize: " << chrSize << endl;

	chrHashData.clear();
	gotoChr(chrNum);

	char tempChar;
	string tempKey;

	int skipN = 0;

	//get first Kmer
	for (int i = 0; i < KmerSize; i++) {
		//show progress
		makeProgress("loadHash", progressCount, chrSize, 100000);

		refFile >> tempChar;
		if (tempChar == 'N' || tempChar == 'M' || tempChar == 'R') {
			skipN = KmerSize;
		}
		tempKey += tempChar;
	}

	while (refFile.good() && tempChar != '>') {
		//show progress
		makeProgress("loadHash", progressCount, chrSize, 100000);

		//If there is any N, we skip the Kmer
		if (tempChar == 'N' || tempChar == 'M' || tempChar == 'R') {
			skipN = KmerSize;
		}

		if (skipN == 0) {
			//Allocate if doesn't exist, otherwise, increment.
			if (chrHashData.find(KmerHash(tempKey, KmerSize))
					== chrHashData.end())
				chrHashData[KmerHash(tempKey, KmerSize)] = vector<unsigned int>(
						1, progressCount);
			else
				chrHashData[KmerHash(tempKey, KmerSize)].push_back(
						progressCount);

		} else {
			skipN--;
		}
		refFile >> tempChar;
		tempKey = tempKey.substr(1, KmerSize - 1) + tempChar;
	}

	cout << "Verifying 0 entry Kmers" << endl;
	cout << "Num of Kmer in loadHash(): " << chrHashData.size() << endl;
	for (HTIter = chrHashData.begin(); HTIter != chrHashData.end(); HTIter++) {
		if (HTIter->second.size() == 0) {
			cout << "hashIter " << (HTIter->first).getKmer()
					<< " has 0 content!" << endl;
		}
	}
}

void hashGen::unloadHash() {
	refAnalyser::unloadHash();
	chrHashData.clear();
}

void hashGen::printStat() {
	refAnalyser::printStat();
	//storePartialTable();
	//cout << "***Here!" << endl;
}

void hashGen::storePartialTable() {
	unsigned int progress = 0;

	ofstream HTfile;
	//cout << "***Here!2" << endl;
	HTfile.open(HTFileName.c_str());
	HTfile << KmerSize << " " << chrHashData.size() << endl;
	for (HTIter = chrHashData.begin(); HTIter != chrHashData.end(); HTIter++) {
		HTfile << HTIter->first.getKmer() << " " << progress << " "
				<< HTIter->second.size() << endl;
		progress += HTIter->second.size();
	}
	HTfile << endl;
	for (HTIter = chrHashData.begin(); HTIter != chrHashData.end(); HTIter++) {
		for (int i = 0; i < HTIter->second.size(); i++)
			HTfile << HTIter->second[i] << " ";
	}
	HTfile.close();
}

vector<unsigned long long> hashGen::query(string read) {
	return query(read, conductedAF);
}

vector<unsigned long long> hashGen::query(string read,
		vector<unsigned long int>& conductedAF) {
	vector<unsigned long long> locList;

	int KmerNum = read.length() / KmerSize;
	int* cumulatedAF;

	cumulatedAF = new int[maxError + 1];

	keyStruct * sortInput;

	sortInput = new keyStruct[KmerNum];

	//Getting the Kmers
	for (int i = 0; i < KmerNum; i++) {
		string KmerString = read.substr(i * KmerSize, KmerSize);
		if (chrHashData.find(KmerHash(KmerString, KmerSize))
				!= chrHashData.end())
			sortInput[i].key_entry_size = chrHashData[KmerHash(KmerString,
					KmerSize)].size();
		else
			sortInput[i].key_entry_size = 0;

		sortInput[i].original_order = i;
	}
	//Sort them
	qsort(sortInput, KmerNum, sizeof(keyStruct), compareEntrySize);

	for (int setError = 0; setError <= maxError; setError++) {
//		cout << "setError: " << setError << endl;

		cumulatedAF[setError] = 0;
		for (int e = setError; e >= 0; e--) {

			int mergeIdx = setError + 1;

			if (mergeIdx < KmerNum) {
//				cout << "mergeIdx: " << mergeIdx << endl;

				string KmerString = read.substr(
						sortInput[e].original_order * KmerSize, KmerSize);

				string mergeString = read.substr(
						sortInput[mergeIdx].original_order * KmerSize,
						KmerSize);

//				cout << "mergeString: " << mergeString << endl;
//				cout << "KmerString: " << KmerString << endl;

				for (int i = 0; i < sortInput[e].key_entry_size; i++) {

//					cout << "sortInput[e].original_order: " << sortInput[e].original_order << endl;

//					cout
//							<< "chrHashData[KmerHash(KmerString, KmerSize)].size(): "
//							<< chrHashData[KmerHash(KmerString, KmerSize)].size()
//							<< endl;

//					cout << "chrHashData[KmerHash(KmerString, KmerSize)][" << i
//							<< "]: "
//							<< chrHashData[KmerHash(KmerString, KmerSize)][i]
//							<< endl;

					int Loc = chrHashData[KmerHash(KmerString, KmerSize)][i]
							+ (sortInput[mergeIdx].original_order
									- sortInput[e].original_order) * KmerSize;

//					cout << "Loc: " << Loc << endl;

					for (int j = 0;
							j
									< chrHashData[KmerHash(mergeString,
											KmerSize)].size(); j++)
//						cout << "mergeString[" << j << "]: "
//								<< chrHashData[KmerHash(mergeString, KmerSize)][j]
//								<< endl;

					if (searchKey(Loc,
							chrHashData[KmerHash(mergeString, KmerSize)],
							setError))
						cumulatedAF[setError]++;

				}
				mergeIdx++;
			} else {
				cumulatedAF[setError] += sortInput[e].key_entry_size;
			}
			//cumulatedAF[setError] += sortInput[e].key_entry_size;

		}

		conductedAF[setError] += cumulatedAF[setError];
	}

	delete[] sortInput;
	try {
		locList.assign(cumulatedAF[maxError], 0);
	} catch (const bad_alloc& ba) {
		cerr << "cumulatedAF[maxError]: " << cumulatedAF[maxError] << endl;
	}
	delete[] cumulatedAF;
	return locList;
}

bool hashGen::searchKey(int target_coor, vector<unsigned int> & entry_coor,
		int errThreshold) {
	int entry_size = entry_coor.size();
	if (entry_size == 0)
		return false;
	int lower_bound = 0;
	int upper_bound = entry_size - 1;
	int mid = lower_bound + entry_size / 2;

	while (lower_bound < upper_bound) {
		if (entry_coor[mid] <= target_coor + errThreshold
				&& entry_coor[mid] >= target_coor - errThreshold)
			break;
		else if (entry_coor[mid] < target_coor)
			lower_bound = mid + 1;
		else
			upper_bound = mid - 1;
		mid = lower_bound + (upper_bound - lower_bound) / 2;
	}

	if (entry_coor[mid] <= target_coor + errThreshold
			&& entry_coor[mid] >= target_coor - errThreshold) {
		return true;
	} else
		return false;
}

void hashGen::storeFullTable() {
	unsigned int progress = 0;
	ofstream HTfile;
}

